package spring.cloud.tasks.executor.trigger;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.TriggerBuilder;
import spring.cloud.tasks.executor.exception.JobException;

import java.text.ParseException;
import java.util.TimeZone;

@Slf4j
@Setter
@Getter
public class CronTrigger extends AbstractTrigger {

    @Override
    public org.quartz.Trigger createQuartzTrigger() {
        return createTrigger();
    }

    private org.quartz.Trigger createTrigger() {
        String cron = taskExecuteEngine.getTaskConfigurationService().getCronExpression();
        if (StringUtils.isBlank(cron)) {
            cron = "* * * * * ? 2099";
        } else {
            cron = cron.trim();
            validateCron(cron);
        }
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        cronScheduleBuilder = cronScheduleBuilder.inTimeZone(TimeZone.getDefault()).withMisfireHandlingInstructionDoNothing();
        org.quartz.CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(taskExecuteEngine.getExecutorId() + "_" + taskExecuteEngine.getTaskId()).withSchedule(cronScheduleBuilder).build();
        if (cronTrigger instanceof org.quartz.spi.MutableTrigger) {
            ((org.quartz.spi.MutableTrigger) cronTrigger).setMisfireInstruction(org.quartz.CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
        }
        return cronTrigger;
    }

    private void validateCron(String cron) {
        try {
            CronExpression.validateExpression(cron);
        } catch (ParseException e) {
            log.error("validate cron failed", e);
            throw new JobException(e);
        }
    }

    @Override
    public boolean isInitialTriggered() {
        return false;
    }

    @Override
    public void enableTask() {
        boolean shouldReschedule = false;

        String cronExpression = taskExecuteEngine.getTaskConfigurationService().getCronExpression();
        String previousCronExpression = taskExecuteEngine.getTaskExecutor().getPreviousJobConfiguration().getCronExpression();
        if ((cronExpression != null && !cronExpression.equals(previousCronExpression)) || (cronExpression == null && previousCronExpression != null)) {
            shouldReschedule = true;
            taskExecuteEngine.getTaskExecutor().getPreviousJobConfiguration().setCronExpression(cronExpression);
        }
        if (shouldReschedule) {
            taskExecuteEngine.getTaskExecutor().reInitializeTrigger();
        }

        // if PausePeriodDatePath or pausePeriodTime changed.
        String prePauseDate = taskExecuteEngine.getTaskExecutor().getPreviousJobConfiguration().getPauseDatePeriod();
        String prePauseTime = taskExecuteEngine.getTaskExecutor().getPreviousJobConfiguration().getPauseTimePeriod();
        String pauseDate = taskExecuteEngine.getTaskConfigurationService().getTaskConfiguration().getPauseDatePeriod();
        String pauseTime = taskExecuteEngine.getTaskConfigurationService().getTaskConfiguration().getPauseTimePeriod();

        boolean shouldSetPausePeriodDate = shouldSetPausePeriodDate(prePauseDate, pauseDate);
        boolean shouldSetPausePeriodTime = shouldSetPausePeriodTime(prePauseTime, pauseTime);
        if (shouldReschedule || shouldSetPausePeriodDate || shouldSetPausePeriodTime) {
            taskExecuteEngine.getExecuteService().updateNextFireTime(taskExecuteEngine.getExecuteContextService().getTaskItemIndexList());
        }
        if (shouldSetPausePeriodDate) {
            taskExecuteEngine.getTaskExecutor().getPreviousJobConfiguration().setPauseDatePeriod(pauseDate);
        }
        if (shouldSetPausePeriodTime) {
            taskExecuteEngine.getTaskExecutor().getPreviousJobConfiguration().setPauseTimePeriod(pauseTime);
        }

        int processCountIntervalSeconds = taskExecuteEngine.getTaskConfigurationService().getTaskConfiguration().getProcessCountIntervalSeconds();
        if (taskExecuteEngine.getTaskExecutor().getPreviousJobConfiguration().getProcessCountIntervalSeconds() != processCountIntervalSeconds) {
            taskExecuteEngine.getTaskExecutor().getPreviousJobConfiguration().setProcessCountIntervalSeconds(processCountIntervalSeconds);
            taskExecuteEngine.getTaskExecutor().rescheduleProcessCountJob();
        }
    }

    private boolean shouldSetPausePeriodDate(String prePauseDate, String pauseDate) {
        boolean updatePauseConditionFirst = (prePauseDate != null && !prePauseDate.equals(pauseDate));
        boolean updatePauseConditionSecond = (prePauseDate == null && pauseDate != null);
        return updatePauseConditionFirst || updatePauseConditionSecond;
    }

    private boolean shouldSetPausePeriodTime(String prePauseTime, String pauseTime) {
        boolean updatePauseConditionThird = (prePauseTime != null && !prePauseTime.equals(pauseTime));
        boolean updatePauseConditionFourth = (prePauseTime == null && pauseTime != null);
        return updatePauseConditionThird || updatePauseConditionFourth;
    }

    @Override
    public void disableTask() {

    }

    @Override
    public void onResharding() {

    }

    @Override
    public boolean isFailoverSupported() {
        // 如果上报运行状态，则支持failover；否则，不支持failover
        return taskExecuteEngine.getTaskConfigurationService().isEnabled();
    }
}
